use crate::{DateTime, Utc, Serialize, Deserialize};
use std::collections::HashMap;

/// Core metrics for code analysis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeMetrics {
    /// Lines of code metrics
    pub lines_of_code: LinesOfCodeMetrics,
    /// Complexity metrics
    pub complexity: ComplexityMetrics,
    /// Quality metrics
    pub quality: QualityMetrics,
    /// Maintainability metrics
    pub maintainability: MaintainabilityMetrics,
    /// Performance metrics
    pub performance: PerformanceMetrics,
    /// Timestamp when metrics were calculated
    pub timestamp: DateTime<Utc>,
}

/// Lines of code metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinesOfCodeMetrics {
    /// Total lines of code
    pub total_lines: usize,
    /// Lines of actual code (excluding comments and blank lines)
    pub code_lines: usize,
    /// Comment lines
    pub comment_lines: usize,
    /// Blank lines
    pub blank_lines: usize,
    /// Mixed lines (code + comments)
    pub mixed_lines: usize,
    /// Lines per file breakdown
    pub per_file: HashMap<String, FileLineMetrics>,
}

/// Lines of code metrics for a single file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileLineMetrics {
    pub total: usize,
    pub code: usize,
    pub comments: usize,
    pub blank: usize,
    pub mixed: usize,
}

/// Complexity metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplexityMetrics {
    /// Cyclomatic complexity
    pub cyclomatic_complexity: f64,
    /// Cognitive complexity
    pub cognitive_complexity: f64,
    /// Halstead complexity
    pub halstead_complexity: HalsteadMetrics,
    /// Nesting depth
    pub max_nesting_depth: usize,
    /// Average nesting depth
    pub avg_nesting_depth: f64,
    /// Function complexity distribution
    pub function_complexity: HashMap<String, f64>,
}

/// Halstead complexity metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HalsteadMetrics {
    /// Number of distinct operators
    pub distinct_operators: usize,
    /// Number of distinct operands
    pub distinct_operands: usize,
    /// Total number of operators
    pub total_operators: usize,
    /// Total number of operands
    pub total_operands: usize,
    /// Program vocabulary
    pub vocabulary: usize,
    /// Program length
    pub length: usize,
    /// Calculated length
    pub calculated_length: f64,
    /// Volume
    pub volume: f64,
    /// Difficulty
    pub difficulty: f64,
    /// Effort
    pub effort: f64,
    /// Time required to program
    pub time: f64,
    /// Number of delivered bugs
    pub bugs: f64,
}

/// Quality metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityMetrics {
    /// Code duplication percentage
    pub duplication_percentage: f64,
    /// Test coverage percentage
    pub test_coverage: f64,
    /// Technical debt ratio
    pub technical_debt_ratio: f64,
    /// Code smells count
    pub code_smells: usize,
    /// Security vulnerabilities
    pub security_issues: usize,
    /// Performance issues
    pub performance_issues: usize,
    /// Documentation coverage
    pub documentation_coverage: f64,
}

/// Maintainability metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaintainabilityMetrics {
    /// Maintainability index (0-100)
    pub maintainability_index: f64,
    /// Coupling between objects
    pub coupling: f64,
    /// Cohesion metrics
    pub cohesion: f64,
    /// Inheritance depth
    pub inheritance_depth: f64,
    /// Class responsibility assignment
    pub responsibility_assignment: f64,
    /// Change impact analysis
    pub change_impact: f64,
}

/// Performance metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
    /// Analysis time in milliseconds
    pub analysis_time_ms: u64,
    /// Memory usage in bytes
    pub memory_usage_bytes: u64,
    /// CPU usage percentage
    pub cpu_usage_percent: f64,
    /// I/O operations count
    pub io_operations: usize,
    /// Cache hit ratio
    pub cache_hit_ratio: f64,
}

/// Metric calculation configuration
#[derive(Debug, Clone)]
pub struct MetricsConfig {
    /// Enable complexity analysis
    pub enable_complexity: bool,
    /// Enable quality analysis
    pub enable_quality: bool,
    /// Enable maintainability analysis
    pub enable_maintainability: bool,
    /// Enable performance tracking
    pub enable_performance: bool,
    /// Maximum file size to analyze (bytes)
    pub max_file_size: usize,
    /// Languages to analyze
    pub languages: Vec<String>,
    /// Custom thresholds
    pub thresholds: MetricsThresholds,
}

/// Thresholds for metric evaluation
#[derive(Debug, Clone)]
pub struct MetricsThresholds {
    /// Maximum acceptable cyclomatic complexity
    pub max_cyclomatic_complexity: f64,
    /// Maximum acceptable cognitive complexity
    pub max_cognitive_complexity: f64,
    /// Minimum acceptable test coverage
    pub min_test_coverage: f64,
    /// Maximum acceptable duplication
    pub max_duplication: f64,
    /// Minimum acceptable maintainability index
    pub min_maintainability_index: f64,
}

impl Default for MetricsConfig {
    fn default() -> Self {
        Self {
            enable_complexity: true,
            enable_quality: true,
            enable_maintainability: true,
            enable_performance: true,
            max_file_size: 1024 * 1024, // 1MB
            languages: vec![
                "rust".to_string(),
                "python".to_string(),
                "javascript".to_string(),
                "typescript".to_string(),
                "java".to_string(),
                "cpp".to_string(),
                "c".to_string(),
                "go".to_string(),
            ],
            thresholds: MetricsThresholds::default(),
        }
    }
}

impl Default for MetricsThresholds {
    fn default() -> Self {
        Self {
            max_cyclomatic_complexity: 10.0,
            max_cognitive_complexity: 15.0,
            min_test_coverage: 80.0,
            max_duplication: 5.0,
            min_maintainability_index: 70.0,
        }
    }
}

/// Metric evaluation result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricEvaluation {
    /// Metric name
    pub name: String,
    /// Current value
    pub value: f64,
    /// Threshold value
    pub threshold: f64,
    /// Whether the metric passes the threshold
    pub passes: bool,
    /// Severity level
    pub severity: MetricSeverity,
    /// Recommendation for improvement
    pub recommendation: Option<String>,
}

/// Severity levels for metric violations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MetricSeverity {
    Info,
    Warning,
    Error,
    Critical,
}

/// Aggregated metrics summary
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsSummary {
    /// Overall quality score (0-100)
    pub overall_score: f64,
    /// Number of files analyzed
    pub files_analyzed: usize,
    /// Total lines of code
    pub total_loc: usize,
    /// Number of functions
    pub function_count: usize,
    /// Number of classes
    pub class_count: usize,
    /// Number of issues found
    pub issues_count: usize,
    /// Metric evaluations
    pub evaluations: Vec<MetricEvaluation>,
    /// Language breakdown
    pub language_breakdown: HashMap<String, LanguageMetrics>,
}

/// Metrics for a specific language
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageMetrics {
    /// Lines of code in this language
    pub lines_of_code: usize,
    /// Number of files
    pub file_count: usize,
    /// Average complexity
    pub avg_complexity: f64,
    /// Quality score
    pub quality_score: f64,
}

impl CodeMetrics {
    /// Create new empty metrics
    pub fn new() -> Self {
        Self {
            lines_of_code: LinesOfCodeMetrics::default(),
            complexity: ComplexityMetrics::default(),
            quality: QualityMetrics::default(),
            maintainability: MaintainabilityMetrics::default(),
            performance: PerformanceMetrics::default(),
            timestamp: Utc::now(),
        }
    }

    /// Calculate overall quality score
    pub fn calculate_quality_score(&self) -> f64 {
        let mut score = 100.0;

        // Deduct points for complexity
        if self.complexity.cyclomatic_complexity > 10.0 {
            score -= (self.complexity.cyclomatic_complexity - 10.0) * 2.0;
        }

        // Deduct points for quality issues
        score -= self.quality.duplication_percentage * 2.0;
        score -= (100.0 - self.quality.test_coverage) * 0.5;
        score -= self.quality.technical_debt_ratio * 10.0;

        // Deduct points for maintainability
        if self.maintainability.maintainability_index < 70.0 {
            score -= (70.0 - self.maintainability.maintainability_index) * 0.5;
        }

        score.max(0.0).min(100.0)
    }
}

impl Default for LinesOfCodeMetrics {
    fn default() -> Self {
        Self {
            total_lines: 0,
            code_lines: 0,
            comment_lines: 0,
            blank_lines: 0,
            mixed_lines: 0,
            per_file: HashMap::new(),
        }
    }
}

impl Default for ComplexityMetrics {
    fn default() -> Self {
        Self {
            cyclomatic_complexity: 0.0,
            cognitive_complexity: 0.0,
            halstead_complexity: HalsteadMetrics::default(),
            max_nesting_depth: 0,
            avg_nesting_depth: 0.0,
            function_complexity: HashMap::new(),
        }
    }
}

impl Default for HalsteadMetrics {
    fn default() -> Self {
        Self {
            distinct_operators: 0,
            distinct_operands: 0,
            total_operators: 0,
            total_operands: 0,
            vocabulary: 0,
            length: 0,
            calculated_length: 0.0,
            volume: 0.0,
            difficulty: 0.0,
            effort: 0.0,
            time: 0.0,
            bugs: 0.0,
        }
    }
}

impl Default for QualityMetrics {
    fn default() -> Self {
        Self {
            duplication_percentage: 0.0,
            test_coverage: 0.0,
            technical_debt_ratio: 0.0,
            code_smells: 0,
            security_issues: 0,
            performance_issues: 0,
            documentation_coverage: 0.0,
        }
    }
}

impl Default for MaintainabilityMetrics {
    fn default() -> Self {
        Self {
            maintainability_index: 100.0,
            coupling: 0.0,
            cohesion: 100.0,
            inheritance_depth: 0.0,
            responsibility_assignment: 100.0,
            change_impact: 0.0,
        }
    }
}

impl Default for PerformanceMetrics {
    fn default() -> Self {
        Self {
            analysis_time_ms: 0,
            memory_usage_bytes: 0,
            cpu_usage_percent: 0.0,
            io_operations: 0,
            cache_hit_ratio: 0.0,
        }
    }
}
